home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 3 / Cream of the Crop 3.iso / comm / wnos5src.zip / N8250.C < prev    next >
Text File  |  1993-11-17  |  16KB  |  654 lines

  1. /* OS- and machine-dependent stuff for the 8250 asynch chip on a IBM-PC
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  *
  4.  * 16550A support plus some statistics added mah@hpuviea.at 15/7/89
  5.  *
  6.  * CTS hardware flow control from dkstevens@ucdavis,
  7.  * additional stats from delaroca@oac.ucla.edu added by karn 4/17/90
  8.  * Feb '91      RLSD line control reorganized by Bill_Simpson@um.cc.umich.edu
  9.  * Sep '91      All control signals reorganized by Bill Simpson
  10.  * Apr '92    Control signals redone again by Phil Karn
  11.  */
  12. #include <stdio.h>
  13. #include <dos.h>
  14. #include "global.h"
  15. #include "config.h"
  16. #ifdef ASY
  17. #include "mbuf.h"
  18. #include "proc.h"
  19. #include "iface.h"
  20. #include "asy.h"
  21. #include "pc.h"
  22. #include "slip.h"
  23. #include "n8250.h"
  24. #include "devparam.h"
  25. #ifdef NRS
  26. #include "nrs.h"
  27. #endif
  28.  
  29. static void asy_tx __ARGS((int dev,void *p1,void *p2));
  30.  
  31. struct asy Asy[ASY_MAX] = {0,0,0,0,0,0};
  32.  
  33. /* ASY interrupt handlers */
  34. static INTERRUPT (*Handle[ASY_MAX])() =
  35.     {asy0vec,asy1vec,asy2vec,asy3vec,asy4vec,asy5vec};
  36.  
  37. /* Initialize asynch port "dev" */
  38. int
  39. asy_init(int dev,struct iface *iface,char *arg1,char *arg2,unsigned bufsize,int trigchar,long speed,int cts,int rlsd)
  40. {
  41.     struct fifo *fp;
  42.     unsigned base;
  43.     struct asy *ap = &Asy[dev];
  44.  
  45.     if(bufsize) {
  46.         ap->iface = iface;
  47.         ap->addr = htoi(arg1);
  48.         ap->vec = htoi(arg2);
  49.         ap->trigchar = trigchar;
  50.  
  51.         ap->chain = (strchr(arg2,'c') != NULLCHAR);
  52.  
  53.         /* Set up receiver FIFO */
  54.         fp = &ap->fifo;
  55.         fp->buf = mxallocw(bufsize);
  56.         fp->bufsize = bufsize;
  57.         fp->wp = fp->rp = fp->buf;
  58.         fp->cnt = fp->hiwat = fp->overrun = 0;
  59.     }
  60.  
  61.     base = ap->addr;
  62.  
  63.     /* Purge the receive data buffer */
  64.     inportb(base+RBR);
  65.  
  66.     DISABLE();
  67.     /* Save original interrupt vector, mask state, control bits */
  68.     ap->save.vec = getirq(ap->vec);
  69.     ap->save.mask = getmask(ap->vec);
  70.     ap->save.lcr = inportb(base+LCR);
  71.     ap->save.ier = inportb(base+IER);
  72.     ap->save.mcr = inportb(base+MCR);
  73.     ap->msr = ap->save.msr = inportb(base+MSR);
  74.  
  75.     /* save speed bytes */
  76.     setbit(base+LCR,LCR_DLAB);
  77.     ap->save.divl = inportb(base+DLL);
  78.     ap->save.divh = inportb(base+DLM);
  79.     clrbit(base+LCR,LCR_DLAB);
  80.  
  81.     /* save modem control flags */
  82.     ap->cts = cts;
  83.     ap->rlsd = rlsd;
  84.  
  85.     /* Set interrupt vector to SIO handler */
  86.     setirq(ap->vec,Handle[dev]);
  87.  
  88.     /* Set line control register: 8 bits, no parity */
  89.     outportb(base + LCR,uchar(LCR_8BITS));
  90.  
  91.     /* determine if 16550A, turn on FIFO mode and clear RX and TX FIFOs */
  92.     outportb(base + FCR,uchar(FIFO_ENABLE));
  93.  
  94.     /* According to National ap note AN-493, the FIFO in the 16550 chip
  95.      * is broken and must not be used. To determine if this is a 16550A
  96.      * (which has a good FIFO implementation) check that both bits 7
  97.      * and 6 of the IIR are 1 after setting the fifo enable bit. If
  98.      * not, don't try to use the FIFO.
  99.      */
  100.     if((inportb(base+IIR) & IIR_FIFO_ENABLED) == IIR_FIFO_ENABLED) {
  101.         ap->is_16550a = TRUE;
  102.         outportb(base + FCR,uchar(FIFO_SETUP));
  103.     } else {
  104.         /* Chip is not a 16550A. In case it's a 16550 (which has a
  105.          * broken FIFO), turn off the FIFO bit.
  106.          */
  107.         outportb(base+FCR,0);
  108.         ap->is_16550a = FALSE;
  109.      }
  110.     /* Turn on receive interrupts and optionally modem interrupts;
  111.      * leave transmit interrupts off until we actually send data.
  112.      */
  113.     if(ap->rlsd || ap->cts) {
  114.         outportb(base+IER,IER_MS|IER_DAV);
  115.     } else {
  116.         outportb(base+IER,IER_DAV);
  117.     }
  118.     /* Turn on 8250 master interrupt enable (connected to OUT2) */
  119.     setbit(base+MCR,MCR_OUT2);
  120.  
  121.     /* Enable interrupt */
  122.     maskon(ap->vec);
  123.     RESTORE();
  124.  
  125.     asy_speed(dev,speed);
  126.  
  127.     if(bufsize) {
  128.         char *ifn = if_name(iface," tx");
  129.         iface->proc1 = newproc(ifn,256,asy_tx,dev,iface,NULL,0);
  130.         xfree(ifn);
  131.     }
  132.     return 0;
  133. }
  134.  
  135. int
  136. asy_stop(struct iface *iface,int tmp)
  137. {
  138.     struct asy *ap = &Asy[iface->dev];
  139.     unsigned base = ap->addr;
  140.  
  141.     if(iface == NULLIF) {
  142.         return -1;
  143.     }
  144.     /*-------------------------------------------------------------------*
  145.     * A few notes to the delay below:                                    *
  146.     * It sometimes happ%ned, that NOS simply hangs when an exit is issued*
  147.     * and more than one remote session is active.                        *
  148.     * the reset_all() in between doexit() resets all the remote connec-  *
  149.     * tions causing a RST packet being sent to all those remote instances*
  150.     * During this work, NOS tries to detach the interfaces and in case   *
  151.     * a 16650A is involved, the hardware FIFO is reset and POOF!         *
  152.     * The delay gives enough time to send the packets (I hope) DK5DC     *
  153.     *--------------------------------------------------------------------*/
  154.     pause(500);                           /* let the chip send all data  */
  155.  
  156.     if(!tmp) {
  157.         ap->iface = NULLIF;
  158.  
  159.         /* Release slip or nrs block */
  160. #ifdef  SLIP
  161.         if(Slip[iface->xdev].iface == iface)
  162.             Slip[iface->xdev].iface = NULLIF;
  163. #endif
  164. #ifdef  NRS
  165.         if(Nrs[iface->xdev].iface == iface)
  166.             Nrs[iface->xdev].iface = NULLIF;
  167. #endif
  168.     }
  169.     stop_timer(&ap->idle);
  170.  
  171.     /* Purge the receive data buffer */
  172.     inportb(base + RBR);
  173.  
  174.     if(ap->is_16550a) {
  175.         /* Purge hardware FIFOs and disable. Apparently some
  176.          * other comm programs can't handle 16550s already in
  177.          * FIFO mode; they expect 16450 compatibility mode.
  178.          */
  179.         outportb(base + FCR,uchar(FIFO_SETUP));
  180.         outportb(base + FCR,0);
  181.     }
  182.  
  183.     DISABLE();
  184.     /* Restore original interrupt vector and 8259 mask state */
  185.     setirq(ap->vec,ap->save.vec);
  186.  
  187.     if(ap->save.mask) {
  188.         maskon(ap->vec);
  189.     } else {
  190.         maskoff(ap->vec);
  191.     }
  192.     /* Restore speed regs */
  193.     setbit(base+LCR,LCR_DLAB);
  194.     outportb(base+DLL,ap->save.divl);    /* Low byte */
  195.     outportb(base+DLM,ap->save.divh);    /* Hi byte */
  196.     clrbit(base+LCR,LCR_DLAB);
  197.  
  198.     /* Restore control regs */
  199.     outportb(base+LCR,ap->save.lcr);
  200.     outportb(base+IER,ap->save.ier);
  201.     outportb(base+MCR,ap->save.mcr);
  202.     RESTORE();
  203.  
  204.     if(!tmp)
  205.         xfree(ap->fifo.buf);
  206.  
  207.     return 0;
  208. }
  209.  
  210. /* Set asynch line speed */
  211. int
  212. asy_speed(int dev,long speed)
  213. {
  214.     struct asy *ap = &Asy[dev];
  215.     unsigned base = ap->addr;
  216.  
  217.     int32 divisor = BAUDCLK / speed;
  218.  
  219.     if(speed <= 0 || dev >= ASY_MAX || ap->iface == NULLIF) {
  220.         return -1;
  221.     }
  222.     ap->speed = speed;
  223.  
  224.     DISABLE();
  225.     /* Purge the receive data buffer */
  226.     inportb(base+RBR);
  227.  
  228.     if(ap->is_16550a) {                               /* clear tx+rx fifos */
  229.         outportb(base + FCR,uchar(FIFO_SETUP));
  230.     }
  231.     /* Turn on divisor latch access bit */
  232.     setbit(base+LCR,LCR_DLAB);
  233.  
  234.     /* Load the two bytes of the register */
  235.     outportb(base+DLL,uchar(divisor & 0xff));            /* Low byte */
  236.     outportb(base+DLM,uchar((divisor >> 8) & 0xff));    /* Hi byte */
  237.  
  238.     /* Turn off divisor latch access bit */
  239.     clrbit(base+LCR,LCR_DLAB);
  240.  
  241.     RESTORE();
  242.     return 0;
  243. }
  244.  
  245. /* Asynchronous line I/O control */
  246. int32
  247. asy_ioctl(struct iface *ifp,int cmd,int set,int32 val)
  248. {
  249.     struct asy *ap = &Asy[ifp->dev];
  250.     int16 base = ap->addr;
  251.  
  252.     base += MCR;
  253.  
  254.     switch(cmd){
  255.     case PARAM_SPEED:
  256.         if(set) {
  257.             asy_speed(ifp->dev,val);
  258.         }
  259.         return ap->speed;
  260.     case PARAM_DTR:
  261.         if(set) {
  262.             writebit(base,MCR_DTR,(int)val);
  263.         }
  264.         return (inportb(base) & MCR_DTR) ? TRUE : FALSE;
  265.     case PARAM_RTS:
  266.         if(set) {
  267.             writebit(base,MCR_RTS,(int)val);
  268.         }
  269.         return (inportb(base) & MCR_RTS) ? TRUE : FALSE;
  270.     case PARAM_DOWN:
  271.         clrbit(base,MCR_RTS);
  272.         clrbit(base,MCR_DTR);
  273.         return FALSE;
  274.     case PARAM_UP:
  275.         setbit(base,MCR_RTS);
  276.         setbit(base,MCR_DTR);
  277.         return TRUE;
  278.     }
  279.     return -1;
  280. }
  281.  
  282. /* Blocking read from asynch line
  283.  * Returns count of characters read
  284.  */
  285. int
  286. get_asy(int dev)
  287. {
  288.     struct fifo *fp = &Asy[dev].fifo;
  289.     int c;
  290.  
  291.     DISABLE();
  292.  
  293.     while(fp->cnt == 0) {
  294.         if(pwait(fp) != 0) {
  295.             RESTORE();
  296.             return -1;
  297.         }
  298.     }
  299.     fp->cnt--;
  300.  
  301.     RESTORE();
  302.  
  303.     c = *fp->rp++;
  304.  
  305.     if(fp->rp >= &fp->buf[fp->bufsize]) {
  306.         fp->rp = fp->buf;
  307.     }
  308.     return c;
  309. }
  310.  
  311. /* Process 8250 receiver interrupts */
  312. static void near
  313. asyrxint(struct asy *asyp)
  314. {
  315.     unsigned char c, lsr;
  316.     int cnt = 0, trigseen = FALSE;
  317.     unsigned base = asyp->addr;
  318.     struct fifo *fp = &asyp->fifo;
  319.  
  320.     asyp->rxints++;
  321.  
  322.     for(;;){
  323.         if((lsr = inportb(base + LSR)) & LSR_OE)
  324.             asyp->overrun++;
  325.  
  326.         if(lsr & LSR_DR) {
  327.             asyp->rxchar++;
  328.  
  329.             if((c = inportb(base + RBR)) == asyp->trigchar) {
  330.                 trigseen = TRUE;
  331.             }
  332.             /* If buffer is full, we have no choice but
  333.              * to drop the character
  334.              */
  335.             if(fp->cnt != fp->bufsize) {
  336.                 *fp->wp++ = c;
  337.                 if(fp->wp >= &fp->buf[fp->bufsize]) {
  338.                     /* Wrap around */
  339.                     fp->wp = fp->buf;
  340.                 }
  341.                 if(++fp->cnt > fp->hiwat) {
  342.                     fp->hiwat = fp->cnt;
  343.                 }
  344.                 cnt++;
  345.             } else {
  346.                 fp->overrun++;
  347.             }
  348.         } else {
  349.             break;
  350.         }
  351.     }
  352.     if(cnt > asyp->rxhiwat) {
  353.         asyp->rxhiwat = cnt;
  354.     }
  355.     if(trigseen) {
  356.         psignal(fp,1);
  357.     }
  358.     return;
  359. }
  360.  
  361. /* Handle 8250 transmitter interrupts */
  362. static void near
  363. asytxint(struct asy *asyp)
  364. {
  365.     unsigned base = asyp->addr;
  366.     struct dma *dp = &asyp->dma;
  367.  
  368.     asyp->txints++;
  369.  
  370.     if(!dp->busy || (asyp->cts && !(asyp->msr & MSR_CTS))){
  371.         /* These events "shouldn't happen". Either the
  372.          * transmitter is idle, in which case the transmit
  373.          * interrupts should have been disabled, or flow control
  374.          * is enabled but CTS is low, and interrupts should also
  375.          * have been disabled.
  376.          */
  377.         clrbit(base+IER,IER_TxE);
  378.         return;    /* Nothing to send */
  379.     }
  380.     if(!(inportb(base+LSR) & LSR_THRE)) {
  381.         /* Not really ready */
  382.         return;
  383.     }
  384.     /* If it's a 16550A, load up to 16 chars into the tx hw fifo
  385.      * at once. With an 8250, it can be one char at most.
  386.      */
  387.     if(asyp->is_16550a) {
  388.         int count = dp->cnt;
  389.  
  390.         if(count > OUTPUT_FIFO_SIZE) {
  391.             count = OUTPUT_FIFO_SIZE;
  392.         }
  393.         /* 16550A: LSR_THRE will drop after the first char loaded
  394.          * so we can't look at this bit to determine if the hw fifo is
  395.          * full. There seems to be no way to determine if the tx fifo
  396.          * is full (any clues?). So we should never get here while the
  397.          * fifo isn't empty yet.
  398.          */
  399.         asyp->txchar += count;
  400.         dp->cnt -= count;
  401.  
  402. #ifdef    XXX        /* This is apparently too fast for some chips */
  403.         dp->data = outbuf(base+THR,dp->data,count);
  404. #else
  405.         while(count-- != 0) {
  406.             outportb(base+THR,*dp->data++);
  407.         }
  408. #endif
  409.     } else do {    /* 8250 */
  410.         asyp->txchar++;
  411.         outportb(base+THR,*dp->data++);
  412.  
  413.     } while(--dp->cnt != 0 && inportb(base+LSR) & LSR_THRE);
  414.  
  415.     if(dp->cnt == 0) {
  416.         dp->busy = 0;
  417.         /* Disable transmit interrupts */
  418.         clrbit(base+IER,IER_TxE);
  419.         psignal(asyp,1);
  420.     }
  421. }
  422.  
  423. /* Handle 8250 modem status change
  424.  *    If the signals are unchanging, we ignore them.
  425.  *    If they change, we use them to condition the line.
  426.  */
  427. static void near
  428. asymsint(struct asy *ap)
  429. {
  430.     unsigned base = ap->addr;
  431.     ap->msr = inportb(base+MSR);
  432.  
  433.     ap->msint_count++;
  434.  
  435.     if(ap->cts && (ap->msr & MSR_DCTS)) {
  436.         /* CTS has changed and we care */
  437.         if(ap->msr & MSR_CTS) {
  438.             /* CTS went up */
  439.             if(ap->dma.busy) {
  440.                 /* enable transmit interrupts and kick */
  441.                 setbit(base+IER,IER_TxE);
  442.                 asytxint(ap);
  443.             }
  444.         } else {
  445.             /* CTS now dropped, disable Transmit interrupts */
  446.             clrbit(base+IER,IER_TxE);
  447.         }
  448.     }
  449.     if(ap->rlsd && (ap->msr & MSR_DRLSD)) {
  450.         /* RLSD just changed and we care, signal it */
  451.         psignal(&(ap->rlsd),1);
  452.  
  453.         /* Keep count */
  454.         if(ap->msr & MSR_RLSD) {
  455.             ap->answers++;
  456.         } else {
  457.             ap->remdrops++;
  458.         }
  459.     }
  460.     if(ap->msr & (MSR_TERI | MSR_RI)) {
  461.         asy_ioctl(ap->iface,PARAM_UP,TRUE,0);
  462.     }
  463. }
  464.  
  465. /* Interrupt handler for 8250 asynch chip (called from asyvec.asm) */
  466. INTERRUPT (far *(asyint)(int dev))()
  467. {
  468.     char iir;
  469.     struct asy *asyp = &Asy[dev];
  470.  
  471.     while(((iir = inportb(asyp->addr+IIR)) & IIR_IP) == 0){
  472.         switch(iir & IIR_ID_MASK){
  473.         case IIR_RDA:    /* Receiver interrupt */
  474.             asyrxint(asyp);
  475.             break;
  476.         case IIR_THRE:    /* Transmit interrupt */
  477.             asytxint(asyp);
  478.             break;
  479.         case IIR_MSTAT:    /* Modem status change */
  480.             asymsint(asyp);
  481.             break;
  482.         }
  483.         /* should happen at end of a single packet */
  484.         if(iir & IIR_FIFO_TIMEOUT) {
  485.             asyp->fifotimeouts++;
  486.         }
  487.     }
  488.     return asyp->chain ? asyp->save.vec : NULL;
  489. }
  490.  
  491. /* Poll the asynch input queues; called on every clock tick.
  492.  * This helps limit the interrupt ring buffer occupancy when long
  493.  * packets are being received.
  494.  */
  495. void
  496. asytimer(void)
  497. {
  498.     struct asy *asyp;
  499.  
  500.     for(asyp = Asy; asyp < &Asy[ASY_MAX]; asyp++) {
  501.         if(asyp->fifo.cnt != 0) {
  502.             psignal(&asyp->fifo,1);
  503.         }
  504.         if(asyp->dma.busy
  505.           && (inportb(asyp->addr+LSR) & LSR_THRE)
  506.           && (!asyp->cts || (asyp->msr & MSR_CTS))) {
  507.             asyp->txto++;
  508.             DISABLE();
  509.             asytxint(asyp);
  510.             RESTORE();
  511.         }
  512.     }
  513. }
  514.  
  515. int
  516. doasystat(int argc,char *argv[],void *p)
  517. {
  518.     struct asy *asyp;
  519.  
  520.     for(asyp = Asy; asyp < &Asy[ASY_MAX]; asyp++) {
  521.         int mcr = inportb(asyp->addr + MCR);
  522.  
  523.         if(asyp->iface == NULLIF) {
  524.             continue;
  525.         }
  526.         tprintf("%s: bps %lu%s%s",
  527.             asyp->iface->name,
  528.             asyp->speed,
  529.             asyp->cts ? " [CTS flow control]" : "",
  530.             asyp->rlsd ? " [RLSD line control]" : "");
  531.  
  532.         if(asyp->trigchar != -1) {
  533.             tprintf(" [trigger 0x%02x]",asyp->trigchar);
  534.         }
  535.         if(asyp->is_16550a) {
  536.             tprintf("\n NS16550A: FifoTO %lu TrigLev 0x%02x",
  537.                 asyp->fifotimeouts,
  538.                 FIFO_TRIGGER_LEVEL);
  539.         }
  540.         tprintf("\n MC: int %lu  DTR %s  RTS %s  CTS %s  DSR %s  RI %s  CD %s\n",
  541.             asyp->msint_count,
  542.             (mcr & MCR_DTR) ? "On" : "Off",
  543.             (mcr & MCR_RTS) ? "On" : "Off",
  544.             (asyp->msr & MSR_CTS)  ? "On" : "Off",
  545.             (asyp->msr & MSR_DSR)  ? "On" : "Off",
  546.             (asyp->msr & MSR_RI)   ? "On" : "Off",
  547.             (asyp->msr & MSR_RLSD) ? "On" : "Off");
  548.  
  549.         tprintf(" RX: int %lu chars %lu hwovrn %lu hwhiwat %lu swovrn %lu swhiwat %u\n",
  550.             asyp->rxints,
  551.             asyp->rxchar,
  552.             asyp->overrun,
  553.             asyp->rxhiwat,
  554.             asyp->fifo.overrun,
  555.             asyp->fifo.hiwat);
  556.  
  557.         tprintf(" TX: int %lu chars %lu THRE TO %lu%s\n",
  558.             asyp->txints,
  559.             asyp->txchar,
  560.             asyp->txto,
  561.             asyp->dma.busy ? " BUSY" : "");
  562.  
  563.         asyp->rxhiwat = 0;
  564.         asyp->fifo.hiwat = 0;
  565.     }
  566.     return 0;
  567. }
  568.  
  569. /* Send a message on the specified serial line */
  570. int
  571. asy_send(int dev,struct mbuf *bp)
  572. {
  573.     int backoff = -1;
  574.     struct asy *asyp = &Asy[dev];
  575.  
  576.     if(dev < 0 || dev >= ASY_MAX) {
  577.         free_p(bp);
  578.         return -1;
  579.     }
  580.     stop_timer(&asyp->idle);
  581.  
  582.     while(asyp->rlsd
  583.       && (asyp->msr & MSR_RLSD) == 0
  584.       && asyp->actfile != NULLCHAR) {
  585.         /* Line down, we need to redial
  586.          * But if it's failing, perform random binary exponential backoff
  587.          */
  588.         if(backoff++ >= 0) {
  589.             int pw;
  590. /*
  591.             alarm(1000L * random(30L << backoff));
  592. */
  593.             alarm(10000L);
  594.             pw = (int)pwait(&asyp->rlsd);
  595.             alarm(0L);
  596.  
  597.             if(pw == 0) {
  598.                 /* CD changed, maybe we got called */
  599.                 continue;
  600.             }
  601.         }
  602.         asyp->originates++;
  603.         redial(asyp,1);
  604.     }
  605.     enqueue(&asyp->sndq,bp);
  606.     start_timer(&asyp->idle);        /* Restart idle timeout */
  607.     return 0;
  608. }
  609.  
  610. /* Serial transmit process, common to all protocols */
  611. static void
  612. asy_tx(int dev,void *p1,void *p2)
  613. {
  614.     struct mbuf *bp = NULLBUF;
  615.     struct asy *asyp = &Asy[dev];
  616.     struct dma *dp = &asyp->dma;
  617.  
  618.     unsigned base = asyp->addr + IER;
  619.  
  620.     for( ; ;) {
  621.         /* Fetch a buffer for transmission */
  622.         while(asyp->sndq == NULLBUF) {
  623.             pwait(&asyp->sndq);
  624.         }
  625.         for(bp = dequeue(&asyp->sndq); bp; bp = free_mbuf(bp)) {
  626.             /* Start the transmitter */
  627.             dp->data = bp->data;
  628.             dp->cnt = bp->cnt;
  629.             dp->busy = 1;
  630.  
  631.             /* If CTS flow control is disabled or CTS is true,
  632.              * enable transmit interrupts here so we'll take an immediate
  633.              * interrupt to get things going. Otherwise let the
  634.              * modem control interrupt enable transmit interrupts
  635.              * when CTS comes up. If we do turn on TxE,
  636.              * "kick start" the transmitter interrupt routine, in case just
  637.              * setting the interrupt enable bit doesn't cause an interrupt
  638.              */
  639.             if(!asyp->cts || (asyp->msr & MSR_CTS)){
  640.                 setbit(base,IER_TxE);
  641.                 asytxint(asyp);
  642.             }
  643.             /* Wait for completion */
  644.             DISABLE();
  645.             while(dp->busy) {
  646.                 pwait(asyp);
  647.             }
  648.             RESTORE();
  649.         }
  650.     }
  651. }
  652.  
  653. #endif /* ASY */
  654.